package DbUtils

import (
	"context"
	"database/sql"
	"fmt"
	_ "github.com/ClickHouse/clickhouse-go/v2"
	_ "github.com/alexbrainman/odbc"
	_ "github.com/denisenkom/go-mssqldb"
	"github.com/go-redis/redis/v8"
	_ "github.com/go-sql-driver/mysql"
	influxdb2 "github.com/influxdata/influxdb-client-go/v2"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"

	"log"
	_ "xorm.io/core"
)

/*
	使用方式
	sq, err := DbUtils.SqlServerProOf(
		"10.11.0.2",
		"sa",
		"123",
		"1433",
		"gdh").Client()
	sq.Query("select * from table")
*/
/*
reconnect
	reConnect := 10
	for {
		if err != nil && reConnect > 0 {
			time.Sleep(5 * time.Second)
			log.Println("mysql connect error:", err)
			log.Printf("%s:%s 正在重连...\n", v.GetString("SourceMysql.host"), v.GetString("SourceMysql.port"))
			Connect, err = ConnectClickHouse(v)
			mysqlConnect = Connect.Connection
			reConnect = reConnect - 1
		} else {
			break
		}
	}
	if reConnect == 0 {
		log.Println("重连上限，退出...")
		return
	}
*/
type DbPro struct {
	username string
	password string
	host     string
	port     string
	database string
}

type MysqlPro struct {
	username string
	password string
	host     string
	port     string
	database string
}

type SqlServerPro struct {
	username string
	password string
	host     string
	port     string
	database string
}

type ClickhousePro struct {
	username string
	password string
	host     string
	port     string
	database string
	//Connection *sql.DB
}

type RedisPro struct {
	username string
	password string
	host     string
	port     string
	//database string
	//Connection *sql.DB
}

type MongoDbPro struct {
	username string
	password string
	host     string
	port     string
	database string
}

type InfluxPro struct {
	url    string
	token  string
	org    string
	bucket string
}

func DbProOf(host string, username string, password string, port string, database string) *DbPro {
	return &DbPro{
		username: username,
		password: password,
		host:     host,
		database: database,
		port:     port,
	}
}

func ClickHouseProOf(host string, username string, password string, port string, database string) *ClickhousePro {
	return &ClickhousePro{
		username: username,
		password: password,
		host:     host,
		database: database,
		port:     port,
	}
}

func RedisProOf(host string, username string, password string, port string) *RedisPro {
	return &RedisPro{
		username: username,
		password: password,
		host:     host,
		port:     port,
	}
}

func MongoDbProOf(host, username, password, port, database string) *MongoDbPro {
	return &MongoDbPro{
		username: username,
		password: password,
		host:     host,
		port:     port,
		database: database,
	}
}

func InfluxProOf(url string, token string) *InfluxPro {
	return &InfluxPro{
		url:   url,
		token: token,
	}
}

// DbClient dbType:{mysql,mssql}
func (this *DbPro) DbClient(dbType string) (*sql.DB, error) {
	username := this.username
	password := this.password
	host := this.host
	port := this.port
	database := this.database
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?parseTime=true", username, password, host, port, database)
	//fmt.Sprintf(dsn)
	db, err := sql.Open(dbType, dsn)
	db.SetMaxOpenConns(20)    //取消每次请求打开一个数据库链接；最大连接数
	db.SetConnMaxIdleTime(10) //空闲连接数
	if err != nil {
		return nil, err
	}
	err = db.Ping()
	if err != nil {
		return nil, fmt.Errorf("error connecting to database: %v", err)
	}
	return db, nil
}

func (this *ClickhousePro) Client() (*sql.DB, error) {
	username := this.username
	password := this.password
	host := this.host
	port := this.port
	database := this.database
	dsn := fmt.Sprintf("http://%s:%s/%s?username=%s&password=%s", host, port, database, username, password)
	//fmt.Sprintf(dsn)
	db, err := sql.Open("clickhouse", dsn)
	db.SetMaxOpenConns(20)    //取消每次请求打开一个数据库链接；最大连接数
	db.SetConnMaxIdleTime(10) //空闲连接数
	if err != nil {
		return nil, err
	}
	err = db.Ping()
	if err != nil {
		return nil, fmt.Errorf("error connecting to database: %v", err)
	}
	return db, nil
}

func (r *RedisPro) RedisClient() (*redis.Client, error) {
	// 构造 Redis 地址

	addr := fmt.Sprintf("%s:%s", r.host, r.port)

	// 使用 go-redis 创建 Redis 客户端
	dsn := redis.NewClient(&redis.Options{
		Addr: addr,
		//Username: r.username, // 如果 Redis 使用了认证
		Password: r.password, // Redis 密码
		DB:       0,          // 默认选择数据库 0
	})

	// 测试 Redis 连接是否成功
	ctx := context.Background()
	_, err := dsn.Ping(ctx).Result()
	if err != nil {
		return nil, fmt.Errorf("error connecting to Redis: %v", err)
	}

	log.Println("Connected to Redis successfully!")
	return dsn, nil
}

func (this *MongoDbPro) MongoClient() (*mongo.Client, error) {
	// 构建 MongoDB 连接字符串
	uri := fmt.Sprintf("mongodb://%s:%s@%s:%s/%s", this.username, this.password, this.host, this.port, this.database)

	// 配置 MongoDB 客户端选项
	clientOptions := options.Client().ApplyURI(uri)

	// 创建 MongoDB 客户端并连接
	client, err := mongo.Connect(context.Background(), clientOptions)
	if err != nil {
		return nil, fmt.Errorf("error connecting to MongoDB: %v", err)
	}

	// 检查连接是否成功
	err = client.Ping(context.Background(), nil)
	if err != nil {
		return nil, fmt.Errorf("failed to ping MongoDB: %v", err)
	}

	// 返回已连接的客户端实例
	return client, nil
}

func (this *InfluxPro) InfluxClient() (influxdb2.Client, error) {
	client := influxdb2.NewClient(this.url, this.token)
	defer client.Close()
	return client, nil
}
